﻿using System;
using System.Reflection;
using System.Diagnostics;
using System.ComponentModel;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;

namespace Zorbo.Data
{
    public class IOMonitor : INotifyPropertyChanged
    {
        DateTime update;

        Int32 speedIn = 0;
        Int32 speedOut = 0;

        Int32 totalIn = 0;
        Int32 totalOut = 0;

        Int32 currentIn = 0;
        Int32 currentOut = 0;

        volatile bool running;


        public int SpeedIn {
            get { return speedIn; }
            internal set {
                if (speedIn != value) {
                    speedIn = value;
                    RaisePropertyChanged(() => SpeedIn);
                }
            }
        }

        public int SpeedOut {
            get { return speedOut; }
            internal set {
                if (speedOut != value) {
                    speedOut = value;
                    RaisePropertyChanged(() => SpeedOut);
                }
            }
        }


        public int TotalBytesIn {
            get { return totalIn; }
        }

        public int TotalBytesOut {
            get { return totalOut; }
        }


        public bool Running { get { return running; } }



        static Timer timer;
        static List<IOMonitor> monitors;

        static IOMonitor() {
            monitors = new List<IOMonitor>();

            timer = new Timer((state) => {

                for (int i = 0; i < monitors.Count; i++)
                    monitors[i].UpdateSpeed();
            },
            null,
            500,
            500);
        }


        public IOMonitor() {
            update = TimeBank.CurrentTime;
        }


        public void Start() {
            monitors.Add(this);
            running = true;
        }

        public void Stop() {
            monitors.Remove(this);
            running = false;
        }


        public void AddInput(int numbytes) {
            if (running) {
                Interlocked.Add(ref totalIn, numbytes);
                Interlocked.Add(ref currentIn, numbytes);

                RaisePropertyChanged(() => TotalBytesIn);
            }
        }

        public void AddOutput(int numbytes) {
            if (running) {
                Interlocked.Add(ref totalOut, numbytes);
                Interlocked.Add(ref currentOut, numbytes);

                RaisePropertyChanged(() => TotalBytesOut);
            }
        }


        protected void UpdateSpeed() {
            DateTime now = TimeBank.CurrentTime;

            if (now.Subtract(update).TotalSeconds >= 1) {
                update = now;

                Interlocked.Exchange(ref speedIn, currentIn);
                Interlocked.Exchange(ref speedOut, currentOut);

                currentIn = 0;
                currentOut = 0;
            }
        }

        #region " INotifyPropertyChanged "

        protected virtual void RaisePropertyChanged<T>(Expression<Func<T>> property) {
            var prop = ((MemberExpression)property.Body).Member as PropertyInfo;

            if (prop == null) return;
            OnPropertyChanged(prop.Name);
        }

        protected virtual void RaisePropertyChanged(string propName) {
            OnPropertyChanged(propName);
        }

        protected virtual void OnPropertyChanged(string propName) {
            var x = PropertyChanged;
            if (x != null) x(this, new PropertyChangedEventArgs(propName));
        }

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }
}
